using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;

using ExchangeHog.Pop3Client.Responses;

namespace ExchangeHog.Pop3Client.Commands
{
    internal sealed class ConnectCommand : Pop3Command<ConnectResponse>
    {
        private TcpClient _client;
        private string _hostname;
        private int _port;
        private bool _useSsl;

        public ConnectCommand(TcpClient client, string hostname, int port, bool useSsl): base(new System.IO.MemoryStream(), false, Pop3State.Unknown)
        {
            if (client == null)
                throw new ArgumentNullException("client");

            if (string.IsNullOrEmpty(hostname))
                throw new ArgumentNullException("hostname");

            if (port < 1)
                throw new ArgumentOutOfRangeException("port");

            _client = client;
            _hostname = hostname;
            _port = port;
            _useSsl = useSsl;
        }

        protected override byte[] CreateRequestMessage() { return null; }

        internal override ConnectResponse Execute(Pop3State currentState)
        {
            EnsurePop3State(currentState);

            try
            {
                _client.Connect(_hostname, _port);

                SetClientStream();
            }
            catch (SocketException e)
            {
                throw new Pop3Exception(string.Format("Unable to connect to {0}:{1}.", _hostname, _port), e);
            }

            return base.Execute(currentState);
        }

        private void SetClientStream()
        {
            if (_useSsl)
            {
				//
				//	wrap NetworkStream in an SSL stream
				//
                try
                {
					//
					//	make sure the inner stream stays available for the Pop3Client to make use of.
					//
                    NetworkStream = new SslStream(_client.GetStream(), true);
                    ((SslStream)NetworkStream).AuthenticateAsClient(_hostname);
                }
                catch (ArgumentException e)
                {
                    throw new Pop3Exception("Unable to create Ssl Stream for hostname: " + _hostname, e);
                }
                catch (AuthenticationException e)
                {
                    throw new Pop3Exception("Unable to authenticate ssl stream for hostname: " + _hostname, e);
                }
                catch (InvalidOperationException e)
                {
                    throw new Pop3Exception("There was a problem  attempting to authenticate this SSL stream for hostname: " + _hostname, e);
                }
            }
            else
                NetworkStream = _client.GetStream();
        } // end of SetClientStream

        protected override ConnectResponse CreateResponse(byte[] buffer)
        {
			return new ConnectResponse(Pop3Response.CreateResponse(buffer), NetworkStream);
        }
    }
}
